<template>
  <div id="app">
    <h2>QiuYa Simple Client</h2>
    <div style="display: flex">
      <div class="left" style="flex: 1">
        <h5>可用指令，输入命令后按ctrl+enter执行指令</h5>
        <ul>
          <li>db.show() //查看所有数据库名</li>
          <li>db.{dbname}.show() //查看所有表名</li>
          <li>db.{dbname}.drop() //删除该库</li>
          <li>db.{dbname}.{colname}.find(condition) //查询，可追加project/sort/skip/limit 等函数</li>
          <li>db.{dbname}.{colname}.count(condition) //数量统计</li>
          <li>db.{dbname}.{colname}.aggregate(condition) //管道统计</li>
          <li>db.{dbname}.{colname}.insert(obj) //插入 </li>
          <li>db.{dbname}.{colname}.update(condition, action) //更新</li>
          <li>db.{dbname}.{colname}.delete(condition) //删除</li>
          <li>db.{dbname}.{colname}.replace(condition, object) //替换，如果没有默认会插入新的</li>
        </ul>
        <main v-cloak >
          <div class="cm-container">
            <h5>在这里输入代码</h5>
<!--            <codemirror :value="code" @change="change" :options="options" ></codemirror>-->
            <div v-once >
              <textarea ref="codemirror"></textarea>
            </div>
          </div>
        </main>
      </div>

      <div class="right" style="flex: 1">
        <h5>查询结果</h5>
        <JsonViewer :expand-depth="10" :copyable="true" :value="result"></JsonViewer>
<!--        <v-jsoneditor v-model="result" :plus="false" height="600px" ></v-jsoneditor>-->
      </div>
    </div>
  </div>
</template>

<script>
import JsonViewer from "vue-json-viewer"
import ReconnectingWebSocket from "reconnectingwebsocket"
import JSONBig from "json-bigint"
import CodeMirror from "codemirror"

function guid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

var acks = {}
function emit(event, data, callback) {
  var uid = guid()
  var str = `${event} ${uid} ${JSON.stringify(data)}`
  acks[uid] = callback
  setTimeout(function () {
    delete acks[uid]
  },30000)
  console.log("uid is ", uid)
  ws.send(str)
}

var ws = new ReconnectingWebSocket("ws://127.0.0.1:28018/websocket/")
ws.onopen = function () {
  console.log("connected")
  // emit("show_dbs",{db: "test"})
  // ws.send("fff".repeat(10000))
}
ws.onclose = function () {
  console.log("closed")
}
ws.onmessage = function (d) {
  // console.log(d)
  var data = d.data;
  var i = data.indexOf(' ')
  if(i == -1) return
  var ack = data.substring(0, i)
  console.log(ack)
  if(ack != "ack") {
    return;
  }
  var _i = i
  i = data.indexOf(' ', i + 1)
  console.log(_i, i)
  if(i == -1) return;
  var uid = data.substring(_i + 1, i)
  var json = data.substring(i + 1)
  console.warn(acks)
  console.warn(uid)
  console.warn(json)
  if(acks[uid]){
    var str = JSON.parse(JSON.stringify(JSONBig.parse(json)))
    acks[uid](str)
  }
}

const COMMAND_SHOW_DBS = "show_dbs"
const COMMAND_SHOW_COLS = "show_cols"
const COMMAND_CHOOSE_DB = 0x01
const COMMAND_AGGREGATE = "aggregate"
const COMMAND_COUNT = "count"
const COMMAND_INSERT = "insert"
const COMMAND_UPDATE = "update"
const COMMAND_DELETE = "delete"
const COMMAND_REPLACE = "replace"
const COMMAND_DROP_DB = "drop_db"

const RESP_OK = 0x00

export default {
  name: 'App',
  components: {
    JsonViewer
  },
  data: function () {
    return {
      code: "",
      result: {},
      ctrlDown: false,
    }
  },
  computed: {
    // code: function () {
    //     return codes[this.mode]
    // },
    options: function () {
      return {
        mode: "javascript",
        tabSize: 2,
        lineNumbers: true,
        lineWrapping: true,
        viewportMargin: Infinity,
        extraKeys: {'Ctrl-Space': 'autocomplete'},
      }
    }
  },
  methods: {
    onkeydown(e){
    },
    onkeyup(e){
      if(e.keyCode == 13 && e.ctrlKey){
        this.send()
      }
    },

    send(){
      if(this.code == "show dbs") {
        this.emit("command", {
          command: COMMAND_SHOW_DBS
        })
      } else if(this.code.indexOf("use ") == 0){
        this.db = this.code.replace(/use\s*/, "")
        this.emit("command", {
          command: COMMAND_CHOOSE_DB,
          data: this.db
        })
      } else {
        this.compile()
      }
    },
    compile(){
      // if(!this.db){
      //     alert("请先选择数据库")
      //     return
      // }
      var t = this;
      var dbName = ""
      var colName = ""
      var command = -1
      var args = null
      var col = new Proxy({},{
        get(target, key) {
          if(key == "show"){
            return function () {
              command = COMMAND_SHOW_COLS
            }
          } else if (key == "drop"){
            return function () {
              command = COMMAND_DROP_DB
            }
          }

          colName = key
          var t =  {
            insert(item){
              if(!Array.isArray(item)){
                item = [item]
              }
              command = COMMAND_INSERT
              args = {
                data: item || []
              }
            },
            find(){
              command = COMMAND_AGGREGATE
              args = {
                when: [{
                  $match: arguments[0] || {}
                }]
              }
              var tt =  {
                sort(items){
                  var item = args.when.find(e => e.hasOwnProperty("$sort"))
                  var ret = []
                  for(let i in items){
                    ret.push([i, items[i]])
                  }
                  if(!item){
                    args.when.push({$sort: ret || []})
                  } else {
                    item.$sort = ret || []
                  }
                },
                skip(n){
                  var item = args.when.find(e => e.hasOwnProperty("$skip"))
                  if(!item){
                    args.when.push({
                      $skip: n || 0
                    })
                  } else {
                    item.$skip = n || 0
                  }
                  return tt
                },
                project(project){
                  var item = args.when.find(e => e.hasOwnProperty("$project"))
                  if(!item){
                    args.when.push({
                      $project: project || {}
                    })
                  } else {
                    item.$project = project || {}
                  }
                  return tt
                },
                limit(n){
                  var item = args.find(e => e.hasOwnProperty("$limit"))
                  if(!item){
                    args.push({
                      $limit: n
                    })
                  } else {
                    item.$limit = n
                  }
                  return tt
                }
              }
              return tt
            },
            aggregate(condition){
              command = COMMAND_AGGREGATE
              // args = condition
              //因为go不支持map排序，所以需要处理为数组类型
              // condition.forEach((k,v) => {
              //     for(var i in v){
              //         if(i === "$group"){
              //             var kv = []
              //             for(var j in v[i]){
              //                 kv.push([j,v[i][j]])
              //             }
              //             v[i] = kv
              //         }
              //     }
              // });
              args = {
                when: condition
              }
              console.log(command, args)
            },
            count(condition){
              command = COMMAND_COUNT
              args ={
                when : condition || {}
              }
            },
            update(condition, action){
              if(!condition || !action){
                return
              }
              command = COMMAND_UPDATE
              args = {
                when: condition,
                then: action
              }
            },
            delete(condition){
              command = COMMAND_DELETE
              args = {
                when: condition || {}
              }
            },
            replace(condition, obj){
              if(!condition || !obj){
                return
              }
              command = COMMAND_REPLACE
              args = {
                when: condition,
                then: obj
              }
            },


          };
          return t
        }
      });
      var db = new Proxy({}, {
        get(target, key) {
          //show db
          if(key === "show"){
            return function () {
              command = ""
              t.emit(COMMAND_SHOW_DBS, {})
            }
          }
          dbName = key
          return col
        },
      });
      console.log(this.code)
      try{
        eval(this.code)
      } catch(e){
        this.result = {
          error: e.message
        }
        return;
      }
      if(!command){
        console.log("cannot find command")
        return;
      }
      this.emit(command, {
        db: dbName,
        collection: colName,
        ...(args||{})
        // data: args
      })
    },

    emit(order, data){
      var t = this;
      emit(order, data, (res) => {
        // console.log(res)
        // res.code =
        console.log(res)
        t.result = (res)
      })
    },

    change: function (code) {
      this.code = code.trim().replace(/\n+$/,"")
    }
  },

  mounted() {
    var editor = CodeMirror.fromTextArea(this.$refs.codemirror, {
      lineNumbers: true,
      extraKeys: {"Ctrl-Space": "autocomplete"},
      mode: {name: "javascript", globalVars: true}
    });
    editor.on("keyup",  (editor, e)  => {
      this.code = editor.getValue()
      this.onkeyup(e)
    })
  }
}
</script>

<style>
  @import "~codemirror/lib/codemirror.css";
/*#app {*/
/*  font-family: Avenir, Helvetica, Arial, sans-serif;*/
/*  -webkit-font-smoothing: antialiased;*/
/*  -moz-osx-font-smoothing: grayscale;*/
/*  text-align: center;*/
/*  color: #2c3e50;*/
/*  margin-top: 60px;*/
/*}*/
</style>
